Skip to content

Fix: Align View, ViewBuilder, ViewModifier, and ModifiedContent with SwiftUI#67

Merged
phranck merged 6 commits into
mainfrom
issue/17-view-modifier-alignment
Jul 22, 2026
Merged

Fix: Align View, ViewBuilder, ViewModifier, and ModifiedContent with SwiftUI#67
phranck merged 6 commits into
mainfrom
issue/17-view-modifier-alignment

Conversation

@phranck

@phranck phranck commented Jul 22, 2026

Copy link
Copy Markdown
Owner

Closes #17.

Summary

  • ViewBuilder matches SwiftUI's result surface: buildBlock() -> EmptyView, tuple-typed TupleView<(repeat each Content)>, buildIf (was buildOptional), buildEither -> _ConditionalContent (renamed from ConditionalView), buildLimitedAvailability -> AnyView, and the optional-expression overload SwiftUI does not expose is gone; buildArray/ViewArray stays as a documented additive convenience
  • TupleView takes SwiftUI's shape (TupleView<T> with public var value: T): the builder captures children on a reflection-free fast path, the public initializer resolves them via Mirror, and an unconditional best-effort Equatable keeps .equatable() working on multi-child containers (Swift 6.0 cannot constrain tuple elements; non-comparable children safely degrade to cache misses)
  • ViewModifier adopts body(content:) with the _ViewModifier_Content placeholder; .modifier(_:) returns the new public ModifiedContent value (conditional View/Equatable conformances). Procedural buffer transformations moved behind the package-internal BufferViewModifier/BufferModifiedView layer; PaddingModifier/BackgroundModifier are internal now
  • Isolation: @preconcurrency @MainActor on View and ViewModifier per the pinned SwiftUI reference; ViewBuilder stays @MainActor as a documented compiler-floor exception (Swift 6.0 infers main-actor isolation onto conforming types' initializers)
  • AnyView gains init(erasing:)
  • Compile fixture for issue 17 extended (custom views, conditional/availability builders, generic content, custom modifiers, nested modified content); docs rewritten; manifest and policy regenerated from fresh 6.0.3 snapshots

Deviations from the issue text

  • No ContentBuilder: the pinned SwiftUI reference snapshots (Xcode 26.6, SwiftUI + SwiftUICore, all platforms) contain no such symbol; the alias appears to be a spec artifact. Not added.

Test plan

  • 1348 tests green on macOS (Swift 6.0.3) and Linux (Docker 6.0.3-noble) via ./scripts/test-linux.sh
  • New suites: ViewBuilderAlignmentTests (builder surface, tuple typing, runtime equality, availability erasure), ViewModifierContentTests (contract, environment propagation, nesting, value semantics, padding regression)
  • verify-compatibility-manifest.sh passes against the regenerated manifest
  • DocC builds warning-free after the symbol-link fixes

phranck added 6 commits July 22, 2026 12:03
- Add buildBlock() -> EmptyView for empty builder blocks
- Reshape TupleView to SwiftUI's tuple-generic TupleView<T> with a
  public value property; the builder captures children on a fast path
  and the public initializer resolves them by reflection
- Give TupleView an unconditional best-effort Equatable so .equatable()
  keeps working on multi-child containers (Swift 6.0 cannot constrain
  tuple elements); non-comparable children safely degrade to cache
  misses
- Rename ConditionalView to SwiftUI's _ConditionalContent spelling and
  rename buildOptional to buildIf
- Erase limited-availability content to AnyView and add AnyView's
  labeled init(erasing:)
- Drop the optional-expression overload SwiftUI does not expose
- Replace the buffer-level public ViewModifier protocol with SwiftUI's
  body(content:) contract, including the _ViewModifier_Content
  placeholder that resolves to the wrapped view at its body position
- Add the public ModifiedContent value type (unconstrained pair,
  conditional View and Equatable conformances) and return it from
  View.modifier(_:)
- Move procedural buffer transformations behind the package-internal
  BufferViewModifier layer with BufferModifiedView; PaddingModifier and
  BackgroundModifier become internal and .padding()/.background() route
  through the internal wrapper
- Cover the new contract with rendering, environment-propagation,
  nesting, and value-semantics tests plus a padding regression test
- Add _ViewModifier_Content to the type_name exclusion list
- Add @preconcurrency to the View and ViewModifier protocols per the
  pinned SwiftUI reference surface
- Document why ViewBuilder stays @mainactor as a compiler-floor
  exception: Swift 6.0 infers main-actor isolation onto conforming
  types' initializers, which the builder must call
- Extend the issue-17 compile fixture with custom views, conditional
  and availability builder forms, generic content, a custom modifier,
  and nested modified content
- Rewrite the modifier sections in CustomViews, Architecture, and
  RenderCycle for body(content:), ModifiedContent, and the internal
  buffer layer
- Replace links to removed or underscored symbols (ModifiedView,
  modify(buffer:context:), _ConditionalContent) that DocC can no longer
  resolve
- Drop overrides for removed symbols: the buffer-level ViewModifier
  requirements, ModifiedView, ConditionalView, the pack-generic
  TupleView members, buildOptional, and the internalized padding and
  background modifiers
- Add overrides for the new surface: ModifiedContent, the
  body(content:) contract, tuple-typed TupleView members, buildIf,
  buildBlock(), AnyView(erasing:), and the AnyView-erasing
  buildLimitedAvailability
- Regenerate the manifest with TUIkitAPICheck against fresh 6.0.3
  macOS and Linux snapshots
@phranck
phranck merged commit 428ce3a into main Jul 22, 2026
6 checks passed
@phranck
phranck deleted the issue/17-view-modifier-alignment branch July 22, 2026 10:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[P1-12] Align View, ContentBuilder, ViewModifier, and ModifiedContent with SwiftUI

1 participant